package server;

import java.util.HashMap;
import java.util.Iterator;

import org.apache.vysper.storage.StorageProviderRegistry;
import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.addressing.EntityFormatException;
import org.apache.vysper.xmpp.addressing.EntityImpl;
import org.apache.vysper.xmpp.authorization.AccountCreationException;
import org.apache.vysper.xmpp.authorization.AccountManagement;
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.stanza.StanzaBuilder;

/**
 * Contains list of active users. Populates this list at init from config file.
 * Populates StorageProviderRegistry for XMPP server
 * 
 * @author Maris
 * 
 */
public class UserManagement {

	private HashMap<Entity, Long> users = new HashMap<Entity, Long>();
	private StorageProviderRegistry providerRegistry;
	private AccountManagement accountManagement;
	public HashMap<String, String> config;
	private Thread checkerthread;
	
	private static UserManagement umobject;
	/**
	 * Implementation of the singleton pattern using lazy instantiation. See
	 * XMPPClient.java for more details
	 */
	private UserManagement(HashMap<String, String> config) {
		this.config = config;
	}

	private UserManagement() {
	}

	public static synchronized UserManagement getInstance(
			HashMap<String, String> config) {
		if (umobject == null) {
			umobject = new UserManagement(config);
		}
		return umobject;
	}

	public static synchronized UserManagement getInstance() {
		if (umobject == null) {
			umobject = new UserManagement();
		}
		return umobject;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	/**
	 * Checks clients hashmap every timeout seconds If client is late
	 * xmpp_server_client_timeout seconds: 
	 * - releases his locked files 
	 * - injects "unavailable" presence
	 * 
	 */
	private class CheckerThread implements Runnable {
		private boolean doRun = true;
		int timeout = 10; // seconds, default 30

		public CheckerThread(int time) {
			timeout = time;
		}

		public void run() {
			while (doRun == true) {
				try {
					Long time;
					Iterator<Entity> it = users.keySet().iterator();
					System.out.println("Printing userlist:");
					while (it.hasNext()) {
						Entity en = it.next();
						time = users.get(en);
						System.out.println("User: "+en.getFullQualifiedName()+
								" last seen: " + (System.currentTimeMillis()-time) + " ms ago.");

//						Stanza stanza = StanzaBuilder.createUnavailablePresenceStanza("Unavailable", null);
						//						try {
//							src.getPresenceCache().get(en).getStatus(value);
//							System.out.println(value);
//						} catch (PresenceCachingException e) {
//							// TODO Auto-generated catch block
//							e.printStackTrace();
//						} catch (XMLSemanticError e) {
//							// TODO Auto-generated catch block
//							e.printStackTrace();
//						}
					}
					Thread.sleep(timeout * 1000);
				} catch (InterruptedException e) {
					doRun = false;
				}
			}

		}
	}

	public void init() {

		// providerRegistry = new JcrStorageProviderRegistry();
		// too complex for our needs
		providerRegistry = new MemoryStorageProviderRegistry();
		accountManagement = (AccountManagement) providerRegistry
				.retrieve(AccountManagement.class);

		String key, value;
		Iterator<String> it = config.keySet().iterator();
		while (it.hasNext()) {
			key = it.next().toString();
			value = config.get(key).toString().trim();
			if (key.startsWith("user_")) {
				addUserForXmppServer(value);
				// addUser(value);
			}
		}

		// start checking thread (looks for clients who are late with ping)
		checkerthread = new Thread(new CheckerThread(10));
		checkerthread.start();
	}

	/**
	 * Adds username to list of users online
	 * 
	 * @param node
	 *            = name to add
	 */
	public void addUser(Entity user) {
		// String JID = node.concat("@");
		// JID = JID.concat(config.get("domain"));

		// Entity en = EntityImpl.parse(JID);
		users.put(user, System.currentTimeMillis());
	}

	/**
	 * Check online user list against supplied name
	 * 
	 * @param username
	 * @return true if username is in list, false otherwise
	 */
	public boolean isOnline(String username) {
		Entity key;
		Iterator<Entity> it = users.keySet().iterator();
		while (it.hasNext()) {
			key = it.next();
			if (username.contentEquals(key.getNode()))
				return true;
		}
		return false;
	}

	/**
	 * Adds username to accountManagement, so that user can log into XMPP server
	 * 
	 * @param username
	 * @return true if successful
	 */
	private boolean addUserForXmppServer(String userName) {
		try {
			String JID = userName.concat("@");
			JID = JID.concat(config.get("domain"));
			if (!accountManagement.verifyAccountExists(EntityImpl.parse(JID))) {
				try {
					accountManagement.addUser(EntityImpl.parse(JID), "default");
					System.out.println("Adding user: " + JID);
				} catch (AccountCreationException e) {
					e.printStackTrace();
				} catch (EntityFormatException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println("User " + userName + " already exists.");
				return false;
			}
		} catch (EntityFormatException e) {
			e.printStackTrace();
		}
		return true;
	}

	public StorageProviderRegistry getProviderRegistry() {
		return providerRegistry;
	}
	
	public void destroy() {
		checkerthread.interrupt();
	}
}
